package com.tsing.common.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;
import com.tsing.common.annotations.SystemLog;
import com.tsing.common.mapper.LogMapper;
import com.tsing.common.service.UserRedisService;
import com.tsing.common.vo.LogVo;
import com.tsing.common.vo.ResultData;
import com.tsing.common.vo.UserVo;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Component
public class LogAspect {

    @Autowired
    private UserRedisService userRedisService;

    @Resource
    private LogMapper logMapper;

    @Value("${frame.log.support:false}")
    private String logSupport;

    @Pointcut("@annotation(com.tsing.common.annotations.SystemLog)")
    public void logPointCut() {
    }

    @Around("logPointCut()")
    public ResultData recordLog(ProceedingJoinPoint point) throws Throwable {
        ResultData result = (ResultData) point.proceed();
        if (!"true".equals(logSupport)) {
            return result;
        }
        try {
            getLog(point);
        } catch (Exception e) {
        }
        return result;
    }

    private void getLog(ProceedingJoinPoint point) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String userId = "";
        String token = request.getHeader("token");
        if (null != token) {
            UserVo userVo = userRedisService.getUserByToken(token);
            if (userVo != null) {
                userId = userVo.getId();
            }
        }
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        LogVo logVo = new LogVo();
        logVo.setUserId(userId);
        logVo.setRequestIp(request.getRemoteAddr());
        logVo.setActionDate(LocalDateTime.now());
        logVo.setActionClass(point.getTarget().getClass().getName());
        logVo.setActionMethod(signature.getName());
        logVo.setDescription(method.getAnnotation(SystemLog.class).description());
        logVo.setMethodType(request.getMethod());
        String params = "";
        if (!request.getMethod().equals(HttpMethod.GET.name())) {
            Object[] args = ArrayUtils.toArray(point.getArgs());
            try {
                params = JSON.toJSONString(args[0], SerializerFeature.WriteMapNullValue);
            } catch (Exception e) {
            }
        } else {
            //参数名
            Parameter[] parameters = method.getParameters();
            //参数值
            Object[] args = ArrayUtils.toArray(point.getArgs());
            Map<String, Object> map = new HashMap<>();
            for (int i = 0; i < parameters.length; i++) {
                map.put(parameters[i].getName(), args[i]);
            }
            params = JSON.toJSONString(map);
        }
        logVo.setParams(params);
        saveLog(logVo);
    }

    /*@AfterReturning(pointcut = "logPointCut()", returning = "object")
    public void doAfterReturning(Object object) {
        if (object != null) {
            LogVo logVo = new LogVo();
            logVo.setReturnData(object.toString());
            saveLog(logVo);
        }
    }*/

    @AfterThrowing(pointcut = "logPointCut()", throwing = "e")
    public void doAfterThrowing(Throwable e) {
        if (!"true".equals(logSupport)) {
            return;
        }
        LogVo logVo = new LogVo();
        logVo.setException(JSONObject.toJSONString(e, new SimplePropertyPreFilter(Throwable.class, "code", "description", "type")));
        saveLog(logVo);
    }

    private void saveLog(LogVo logVo) {
        logMapper.insert(logVo);
    }

}
